using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Xfinity.Entities;
using Xfinity.Engine;
using Xfinity.Engine.Plugins;
using Xfinity.ParticleSystems;
using System;

namespace Xfinity.Actors
{
    [Serializable]
    public partial class Player : Character
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="Player"/> class.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="game">The game.</param>
        public Player(string name, Game game)
            : base(name, game)
        {
            Xfinity.Settings.EngineSettings.PlayerName = name;
            game.Components.Add(this);
            this.Enabled = true;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Player"/> class.
        /// </summary>
        /// <param name="game">Game the game component should be attached to.</param>
        public Player(Game game)
            : this("", game) { }

        /// <summary>
        /// Initialises the entity.
        /// </summary>
        public override void Initialize()
        {
            upDir = Vector3.Up;
            actingForces.Add(new Vector3(0, -9.8f, 0));
#if !XBOX
            externalForcesEnabled = Xfinity.Settings.EngineSettings.ExternalForcesEnabled;
#endif
            //player weighs 100kg
            Mass = 100;

            InputHandler.RegisterKey(Keys.W, MoveForward, StopMovingForward);
            InputHandler.RegisterKey(Keys.S, MoveBackwards, StopMovingBackwards);
            InputHandler.RegisterKey(Keys.A, StrafeLeft, StopStrafingLeft);
            InputHandler.RegisterKey(Keys.D, StrafeRight, StopStrafingRight);
            InputHandler.RegisterKey(Keys.Up, LookUp, StopLookingUp);
            InputHandler.RegisterKey(Keys.Down, LookDown, StopLookingDown);
            InputHandler.RegisterKey(Keys.Left, LookLeft, StopLookingLeft);
            InputHandler.RegisterKey(Keys.Right, LookRight, StopLookingRight);
            InputHandler.RegisterKey(Keys.Z, delegate { GraphicsHelper.ActiveCamera.Zoom(MathHelper.ToRadians(5)); NormalNaturalVelocity /= 10; }, delegate { GraphicsHelper.ActiveCamera.Zoom(MathHelper.PiOver4); NormalNaturalVelocity *= 10; });
            InputHandler.RegisterKey(Keys.X, delegate { GraphicsHelper.ActiveCamera.Zoom(MathHelper.ToRadians(90)); }, delegate { GraphicsHelper.ActiveCamera.Zoom(MathHelper.PiOver4); });
            InputHandler.RegisterKey(Keys.Q, delegate { up = true; }, delegate { up = false; });
            InputHandler.RegisterKey(Keys.E, delegate { down = true; }, delegate { down = false; });
            InputHandler.RegisterKey(Keys.LeftShift, delegate { NormalNaturalVelocity *= 2; }, delegate { NormalNaturalVelocity /= 2; });
            InputHandler.RegisterKey(Keys.LeftControl, delegate { NormalNaturalVelocity /= 2; }, delegate { NormalNaturalVelocity *= 2; });
            InputHandler.RegisterKey(Keys.LeftAlt, delegate { NormalNaturalVelocity /= 100; }, delegate { NormalNaturalVelocity *= 100; });
            InputHandler.RegisterMouseMovedX(delegate
            {
#if !XBOX
                if (InputHandler.MousePosition.X > .5f)
                {
                    lookRight = true;
                }
                else if (InputHandler.MousePosition.X < .5f)
                {
                    lookLeft = true;
                }
#endif
            }
            );
            InputHandler.RegisterMouseXCentered(delegate { lookLeft = false; lookRight = false; });

            InputHandler.RegisterMouseMovedY(delegate
            {
#if !XBOX
                if (InputHandler.MousePosition.Y > .5f)
                {
                    lookDown = true;
                }
                else if (InputHandler.MousePosition.Y < .5f)
                {
                    lookUp = true;
                }
#endif
            }
            );
            InputHandler.RegisterMouseYCentered(delegate { lookUp = false; lookDown = false; });
            base.Initialize();
        }
        #region Move/look left/right
        /// <summary>
        /// Strafes left.
        /// </summary>
        [DebuggerStepThrough]
        public void StrafeLeft()
        {
            left = true;

        }
        /// <summary>
        /// Stop strafing left.
        /// </summary>
        [DebuggerStepThrough]
        public void StopStrafingLeft()
        {
            left = false;

        }
        /// <summary>
        /// Strafes right.
        /// </summary>
        [DebuggerStepThrough]
        public void StrafeRight()
        {
            right = true;

        }
        /// <summary>
        /// Stops strafing right.
        /// </summary>
        [DebuggerStepThrough]
        public void StopStrafingRight()
        {
            right = false;

        }
        /// <summary>
        /// Moves forward.
        /// </summary>
        [DebuggerStepThrough]
        public void MoveForward()
        {
            forwards = true;
        }
        /// <summary>
        /// Stops moving forward.
        /// </summary>
        [DebuggerStepThrough]
        public void StopMovingForward()
        {
            forwards = false;
        }
        /// <summary>
        /// Moves backwards.
        /// </summary>
        [DebuggerStepThrough]
        public void MoveBackwards()
        {
            backwards = true;

        }
        /// <summary>
        /// Stops moving backwards.
        /// </summary>
        [DebuggerStepThrough]
        public void StopMovingBackwards()
        {
            backwards = false;
        }
        /// <summary>
        /// Looks up.
        /// </summary>
        [DebuggerStepThrough]
        public void LookUp()
        {
            lookUp = true;
        }
        /// <summary>
        /// Stops looking up.
        /// </summary>
        [DebuggerStepThrough]
        public void StopLookingUp()
        {
            lookUp = false;
        }
        /// <summary>
        /// Looks down.
        /// </summary>
        [DebuggerStepThrough]
        public void LookDown()
        {
            lookDown = true;
        }
        /// <summary>
        /// Stops looking down.
        /// </summary>
        [DebuggerStepThrough]
        public void StopLookingDown()
        {
            lookDown = false;
        }
        /// <summary>
        /// Looks left.
        /// </summary>
        [DebuggerStepThrough]
        public void LookLeft()
        {
            lookLeft = true;
        }
        /// <summary>
        /// Stops looking left.
        /// </summary>
        [DebuggerStepThrough]
        public void StopLookingLeft()
        {
            lookLeft = false;
        }
        /// <summary>
        /// Looks right.
        /// </summary>
        [DebuggerStepThrough]
        public void LookRight()
        {
            lookRight = true;
        }
        /// <summary>
        /// Stops looking right.
        /// </summary>
        [DebuggerStepThrough]
        public void StopLookingRight()
        {
            lookRight = false;
        }
        #endregion


        static bool left, right, forwards, backwards, up, down, lookUp, lookDown, lookLeft, lookRight;
        static Vector3 upDir, forwardDir, rightDir, movementDirection;

        static double angularVelocity;

        static double zoomMouse = 1;

        public static double ZoomMouse
        {
            get { return zoomMouse; }
        }
        static bool externalForcesEnabled = Xfinity.Settings.EngineSettings.ExternalForcesEnabled;

        public static bool ExternalForcesEnabled
        {
            get
            {
                return externalForcesEnabled;
            }
            set
            {
                externalForcesEnabled = value;
            }
        }
        /// <summary>
        /// Called when the GameComponent needs to be updated.  Override this method with component-specific update code.
        /// </summary>
        /// <param name="gameTime">Time elapsed since the last call to Microsoft.Xna.Framework.GameComponent.Update(Microsoft.Xna.Framework.GameTime)</param>
        public override void Update(GameTime gameTime)
        {
            gt = gameTime;

            if (InputHandler.MousePosition != Vector2.Zero)
            {
                angularVelocity = gameTime.ElapsedGameTime.TotalMilliseconds * zoomMouse;
                double rads = -GraphicsHelper.ToRadians(angularVelocity);
                float yaw = 0;
                float pitch = 0;
                float roll = 0;
                if (lookLeft||lookRight)
                {
                    //GraphicsHelper.ActiveCamera.RotateYaw((float)(rads* InputHandler.RelativeMousePositionX));
                    yaw = (float)(rads * InputHandler.RelativeMousePositionX);
                }
                //else if (lookRight)
                //{
                //    GraphicsHelper.ActiveCamera.RotateYaw((float)-GraphicsHelper.ToRadians(angularVelocity * InputHandler.RelativeMousePositionX));
                //}



                if ((lookUp||lookDown) && Math.Abs(Vector3.Dot(GraphicsHelper.ActiveCamera.TargetDirection, upDir)) < (.999999))
                {
                    //GraphicsHelper.ActiveCamera.RotatePitch((float)(rads * InputHandler.RelativeMousePositionY));
                    pitch = (float)(rads * InputHandler.RelativeMousePositionY);
                }
                Matrix matrix;
                Matrix.CreateFromYawPitchRoll(yaw, -pitch, roll, out matrix);

                GraphicsHelper.ActiveCamera.ApplyRotationMatrix(ref matrix);

                forwardDir = GraphicsHelper.ActiveCamera.TargetDirection;
                //else if (lookDown && Vector3.Dot(GraphicsHelper.ActiveCamera.TargetDirection, upDir) > (-.99999))
                //{
                //    GraphicsHelper.ActiveCamera.RotatePitch((float)-GraphicsHelper.ToRadians(angularVelocity * InputHandler.RelativeMousePositionY));
                //}
            }

            movementDirection = Vector3.Zero;

            if (left)
            {
                rightDir = Vector3.Cross(GraphicsHelper.ActiveCamera.TargetDirection, upDir);
                movementDirection -= rightDir;
            }
            if (right)
            {
                rightDir = Vector3.Cross(GraphicsHelper.ActiveCamera.TargetDirection, upDir);
                movementDirection += rightDir;
            }

            if (forwards)
            {
                movementDirection += forwardDir;
            }
            if (backwards)
            {
                movementDirection -= forwardDir;
            }

            if (up)
            {
                movementDirection += upDir;
            }
            if (down)
            {
                movementDirection -= upDir;
            }

            if (movementDirection != Vector3.Zero)
            {
                movementDirection.Normalize();
                movementDirection = Vector3.Multiply(movementDirection, NormalNaturalVelocity * (float)gameTime.ElapsedRealTime.TotalSeconds);

            }
            Vector3 previousVelocity = Velocity;
            Velocity = movementDirection;
            

            // model gravity, explosions, and other such things.
            if (externalForcesEnabled)
            {
                seconds = (float)gameTime.ElapsedGameTime.TotalSeconds;
                CalculateForcedVelocity();
                CalculateGravitationalVelocity();
                System.Diagnostics.Debug.WriteLine(forcedVelocity * seconds, "Forced velocity");
                movementDirection += forcedVelocity * seconds;
                System.Diagnostics.Debug.WriteLine(gravitationalVelocity * seconds, "Gravitational velocity");
                movementDirection += gravitationalVelocity * seconds;
            }
            GraphicsHelper.ActiveCamera.MoveAndRotate(movementDirection, movementDirection);

            //Game.Window.Title = "FPS: " + FpsCounter.Fps.ToString() + " Position: " + GraphicsHelper.ActiveCamera.Position.ToString() + " Orientation: " + forwardDir.ToString() + " Pitch: " + GraphicsHelper.ToDegrees(GraphicsHelper.ActiveCamera.Pitch) + " Game time: " + gameTime.TotalGameTime.ToString();
            base.Update(gameTime);
        }
        public Vector3 Forward
        {
            get { return forwardDir; }
            set { forwardDir = value; }
        }
        GameTime gt;

        //private static double zoomFactor = 1;

        /// <summary>
        /// Gets or sets the zoom factor.
        /// </summary>
        /// <value>The zoom factor.</value>
        public static double ZoomFactor
        {
            [DebuggerStepThrough]
            get { return GraphicsHelper.ActiveCamera.ZoomFactor; }
            [DebuggerStepThrough]
            set { GraphicsHelper.ActiveCamera.ZoomFactor = value; zoomMouse = ZoomFactor * mouseSensitivity; }
        }

        private static double mouseSensitivity = 1;

        /// <summary>
        /// Gets or sets the mouse sensitivity.
        /// </summary>
        /// <value>The mouse sensitivity.</value>
        public static double MouseSensitivity
        {
            [DebuggerStepThrough]
            get { return mouseSensitivity; }
            [DebuggerStepThrough]
            set { mouseSensitivity = value; zoomMouse = ZoomFactor * mouseSensitivity; }
        }
    }
}